'''
错误的版本，过拟合
此版本指考虑了每天的数量，做深度学习
'''
import numpy as np
import pandas as pd
import torch
import matplotlib.pyplot as plt

# 构建神经网络
def neural_network():
    data_path = 'chapter3_bike/data.csv'
    rides = pd.read_csv(data_path)
    rides.head()
    counts = rides['cnt'][:50]
    # x此处除以len(counts)，是为了将范围作归一化处理
    # 因为后面的权重和偏置量的取值范围在(-1,1)
    # 若不处理x，则这里的x的取值范围是(1-50)，则需学习更长的时间才能达到效果
    x = torch.FloatTensor(np.arange(len(counts), dtype=float) / len(counts))
    y = torch.FloatTensor(np.array(counts, dtype=float))
    neure_size = 10 # 神经元数量
    # 初始化输入层到隐含层的权重矩阵，它的尺寸是(1,10)
    weights = torch.randn((1, neure_size), requires_grad=True)
    # 初始化隐含层节点的偏置向量，它是尺寸为10的一维向量
    biases = torch.randn((neure_size), requires_grad=True)
    # 初始化隐含层到输出层的权重矩阵，它的尺寸是(10,1)
    weights2 = torch.randn((neure_size, 1), requires_grad=True)

    print('weights:', weights)
    print('biases:', biases)
    print('weights2:', weights2)

    # 开始学习
    learning_rate = 0.001
    losses = []
    # 将x,y重新编排为(50,1)的矩阵
    x = x.view(50, -1)
    y = y.view(50, -1)
    for i in range(10000):
        # 完成计算后的hidden
        hidden = x * weights + biases
        hidden = torch.sigmoid(hidden)
        training = hidden.mm(weights2)
        loss = torch.mean((training - y) ** 2)
        losses.append(loss.data.numpy())
        if i % 10000 == 0:
            print('loss:', loss)
        loss.backward()
        weights.data.add_(-learning_rate * weights.grad.data)
        biases.data.add_(-learning_rate * biases.grad.data)
        weights2.data.add_(-learning_rate * weights2.grad.data)

        weights.grad.data.zero_()
        biases.grad.data.zero_()
        weights2.grad.data.zero_()
    draw_losses(losses)
    draw_origin_data()
    draw_training(x, y, training)
    draw_predication(rides, neure_size, weights, weights2)

# 画出前50日的单车数量图
def draw_origin_data():
    data_path = 'chapter3_bike/data.csv'
    rides = pd.read_csv(data_path) # 加载csv文件
    rides.head()    # 读取head
    counts = rides['cnt'][:50]  # 读取cnt列的前50行数据
    x = np.arange(len(counts))  # 总数50，按步长为1，分解出数组
    y = np.array(counts)        # 将counts转为数组
    plt.figure(figsize=(10, 7))
    plt.plot(x, y, 'o-')
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.show()

# 画出损失函数值
def draw_losses(losses):
    plt.plot(losses)
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.show()
# 画出训练曲线
def draw_training(x, y, training):
    x_data = x.data.numpy()
    plt.figure(figsize=(10,7))
    xplot, = plt.plot(x_data, y.data.numpy(), 'o')
    yplot, = plt.plot(x_data, training.data.numpy())
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.legend([xplot, yplot], ['Data', 'Predication under 100000 epochs'])
    plt.show()
# 画出预测曲线
def draw_predication(rides, neure_size, weights, weights2):
    count = 50
    counts_predicat = rides['cnt'][50:100]
    x = torch.FloatTensor((np.arange(count, dtype=float) + count) / count)
    y = torch.FloatTensor(np.array(counts_predicat, dtype=float))
    hidden = x.expand(neure_size, len(x)).t() * weights.expand(len(x), neure_size)
    hidden = torch.sigmoid(hidden)
    predictions = hidden.mm(weights2)
    loss = torch.mean((y - predictions) ** 2)
    print('loss:', loss)
    # 绘画
    plt.figure(figsize=(10,7))
    xplot, = plt.plot(x.data.numpy(), y.data.numpy(), 'o')
    yplot, = plt.plot(x.data.numpy(), predictions.data.numpy())
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.legend([xplot, yplot], ['Data', 'Prediction'])
    plt.show()

if __name__ == '__main__':
    # draw_origin_data()
    neural_network()
